package cn.mw.cmdb.service.impl;

import cn.hutool.extra.spring.SpringUtil;
import cn.mw.cmdb.entity.*;
import cn.mw.cmdb.enums.TableViewTypeEnum;
import cn.mw.cmdb.enums.VideoInstanceStatusEnum;
import cn.mw.cmdb.enums.VideoUsedYearsEnum;
import cn.mw.cmdb.param.InstanceSearchParam;
import cn.mw.cmdb.param.TableSearchParam;
import cn.mw.cmdb.service.TableViewManageService;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.view.ViewConvertContext;
import cn.mw.cmdb.view.ViewManager;
import cn.mw.components.mongodb.entity.CriteriaOpsType;
import cn.mw.components.mongodb.entity.SubCondition;
import cn.mw.microMonitorCommon.api.common.Constants;
import cn.mw.microMonitorCommon.enums.MwVideoItemEnum;
import cn.mw.microMonitorCommon.enums.VideoStatusEnum;
import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.mw.cmdb.processor.MonitorValue.InBandIpKey;
import static cn.mw.cmdb.util.CMDBTool.updateCabinetStateByList;
import static cn.mw.cmdb.view.InstanceListViewConverter.serviceManage;

@Service
@Slf4j
public class TableViewManageServiceImpl implements TableViewManageService {
    @Autowired
    private ViewManager viewManager;

    public static String itemAssetsStatusKey = "itemAssetsStatus";
    public static String mwInBandIpsKey = "mw_inBandIps";

    @Override
    public Object parseViewData(TableSearchParam param) throws Exception {
        TableViewTypeEnum tableViewTypeEnum = TableViewTypeEnum.getTableViewTypeEnumByCode(param.getViewType());
        switch (tableViewTypeEnum) {
            case videoOverview -> {
                return parseViewDataByVideo(param);
            }
            default -> {
                return parseViewDataByList(param);
            }
        }
    }

    @Autowired
    private CmdbServiceManage cmdbServiceManage;

    private Object parseViewDataByList(TableSearchParam param) throws Exception {
        Map<String, SubCondition> conditions = new HashMap<>();
//        //shishan代码开始
        if (param.getCondition() != null) {
            Map<String, SubCondition> condition2 = new HashMap<>(param.getCondition());
            condition2.forEach((k, v) -> {
                if (InBandIpKey.equals(k)) {
                    conditions.put(mwInBandIpsKey, v);
                }
            });
        }
        long time1 = System.currentTimeMillis();
//        param.convert();
        TableHeaderAndDataManage tableHeaderAndDataManage = SpringUtil.getBean(TableHeaderAndDataManage.class);
        List data = tableHeaderAndDataManage.listTableData(param);
        long time2 = System.currentTimeMillis();
        ViewConvertContext context = new ViewConvertContext();
        context.put(serviceManage, cmdbServiceManage);
//        param.setCondition(conditions);
//        //页面查询，如果字段时不是数据库中存在的，而是在数据获取后组装的，比如资产状态(告警状态是在正常的状态下，细分的一个状态，
//        // 实时获取zabbix数据，有告警，则将正常状态改为警告)
//        List<MwKeyValueParam> queryKeyValue = param.getQueryKeyValue();
//
//        //是否首页资产状态跳转查询
//        if(param.isEnableHomePageStatusLink()){
//            String itemAssetsStatus = param.getItemAssetsStatus();
//            List<String> abnormalStatusList = Arrays.asList(ABNORMAL.getName(), ALERT.getName());
//            if(ABNORMAL.getName().equals(itemAssetsStatus)){
//                data = (List) data.stream().filter(map -> abnormalStatusList.contains(((Map<String, Object>) map).get(itemAssetsStatusKey))).collect(Collectors.toList());
//            }
//        }
//        if (CollectionUtils.isNotEmpty(queryKeyValue)) {
//            data = (List) data.stream().filter(map -> queryKeyValue.stream().allMatch(field ->
//                    ((Map<String, Object>) map).get(field.getName()) != null
//                            && ((Map<String, Object>) map).get(field.getName()).toString().contains(field.getValue()))).collect(Collectors.toList());
//        }
//        //shishan代码结束
        Object listViewObject = viewManager.getListViewObject(data, InstanceRelationInfo.class, param, context);
        updateCabinetStateByList(listViewObject);
        long time3 = System.currentTimeMillis();
        log.info("查询数据耗时:" + (time2 - time1) + "ms;数据转换耗时:" + (time3 - time2) + "ms");
        return listViewObject;
//        return viewManager.getListViewObject(data, null, param, null);

    }


    @Override
    public Object getVideoListCount(TableSearchParam param) throws Exception {
        // 扩展属性查询
        // 离线资产数量
        MwVideoInstanceStatusVo mwVideoInstanceStatusVo = new MwVideoInstanceStatusVo();

        // 离线数 离线+未知
        mwVideoInstanceStatusVo.setOfflineCount(getCountByState(JSON.parseObject(JSON.toJSONString(param), TableSearchParam.class), CMDBTool.getFieldName(VideoModelProperty::getMw_video_online_status), Arrays.asList(VideoStatusEnum.OffLine.getDesc(), VideoStatusEnum.Unknown.getDesc(), null)));

        // 视频异常资产数 视频异常按类型汇总
        setAbnormalCountMap(mwVideoInstanceStatusVo, JSON.parseObject(JSON.toJSONString(param), TableSearchParam.class));
        return mwVideoInstanceStatusVo;
    }

    private void setAbnormalCountMap(MwVideoInstanceStatusVo mwVideoInstanceStatusVo, TableSearchParam param) throws Exception {
        InstanceSearchParam instanceSearchParam = parseSearchParam(param, CMDBTool.getFieldName(VideoModelProperty::getMw_video_screen_status), Arrays.asList(VideoStatusEnum.Abnormal.getDesc(), VideoStatusEnum.Unknown.getDesc(), null));
        Map<String, Integer> abnormalCountMap = new HashMap<>();
        abnormalCountMap.put(MwVideoItemEnum.MW_Screen_Loss.getDesc(), 0);
        abnormalCountMap.put(MwVideoItemEnum.MW_Lens_Offset.getDesc(), 0);
        abnormalCountMap.put(MwVideoItemEnum.MW_Screen_Occlusion.getDesc(), 0);
        abnormalCountMap.put(MwVideoItemEnum.MW_Snowflake_Interference.getDesc(), 0);
        int videoAbnormalCount = 0;
        List<String> abnormalList = new ArrayList<>();
        if (instanceSearchParam != null) {
            InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
            List<InstanceInfo> instanceInfoList = instanceService.doSelectList(instanceSearchParam);
            for (InstanceInfo instanceInfo : instanceInfoList) {
                VideoModelProperty videoModelProperty = JSON.parseObject(JSON.toJSONString(instanceInfo.getData()), VideoModelProperty.class);
                // 视频状态为空 或者非正常状态 都视为视频异常数
                if (videoModelProperty.getMw_video_screen_status() == null || !StringUtils.equals(videoModelProperty.getMw_video_screen_status(), VideoStatusEnum.Normal.getDesc())) {
                    videoAbnormalCount++;
                    // 记录异常原因
                    if (StringUtils.isNotBlank(videoModelProperty.getMw_video_abnormal_cause())) {
                        abnormalList.addAll(Arrays.stream(videoModelProperty.getMw_video_abnormal_cause().split(";")).toList().stream().filter(StringUtils::hasText).toList());
                    }
                }
            }
        }
        for (String abnormalStr : abnormalList) {
            if (!abnormalCountMap.containsKey(abnormalStr)) {
                abnormalCountMap.put(abnormalStr, 0);
            }
            abnormalCountMap.put(abnormalStr, abnormalCountMap.get(abnormalStr) + 1);
        }
        mwVideoInstanceStatusVo.setVideoAbnormalCount(videoAbnormalCount);
        mwVideoInstanceStatusVo.setAbnormalCountMap(abnormalCountMap);
    }

    private InstanceSearchParam parseSearchParam(TableSearchParam param, String fieldName, List<String> status) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        InstanceSearchParam instanceSearchParam = new InstanceSearchParam();
        List<String> queryValues = status;
        if (param.getCondition() != null && param.getCondition().containsKey(fieldName)) {
            SubCondition subCondition = param.getCondition().get(fieldName);
            if (subCondition.getValue() != null && queryValues.contains(subCondition.getValue().toString())) {
                queryValues = Collections.singletonList(subCondition.getValue().toString());
            } else {
                return null;
            }
        }
        Object modelId = param.getId();
        String[] assetStateQuery = null;
        if (param.getId().contains(Constants.ID_SPLIT)) {
            modelId = param.getId().split(Constants.ID_SPLIT)[0];
            assetStateQuery = param.getId().split(Constants.ID_SPLIT)[1].split(Constants.PROPERTY_SPLIT);
        }
        Map<String, Object> searchData = new HashMap<>();
        if (assetStateQuery != null) {
            if (assetStateQuery.length > 1) {
                searchData.put(assetStateQuery[0], assetStateQuery[1]);
            } else {
                searchData.put(assetStateQuery[0], null);
            }
        }
        PropertyUtils.copyProperties(instanceSearchParam, param);
        instanceSearchParam.setData(searchData);
        if (modelId != null && StringUtils.hasText(modelId.toString())) {
            SubCondition subCondition = new SubCondition(CriteriaOpsType.is.name(), modelId);
            instanceSearchParam.addSubCondition(CMDBTool.getFieldName(InstanceInfo::getModelId), subCondition);
        }
        instanceSearchParam.setEnableMonitorStatus(true);
        searchData.put(fieldName, queryValues);
        return instanceSearchParam;
    }

    private Integer getCountByState(TableSearchParam param, String fieldName, List<String> status) throws Exception {
        InstanceSearchParam instanceSearchParam = parseSearchParam(param, fieldName, status);
        if (instanceSearchParam == null) {
            return 0;
        }
        InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
        return instanceService.aggregateCount(instanceSearchParam);
    }

    private Object parseViewDataByVideo(TableSearchParam param) throws Exception {
        // 查询分组下所有实例
        InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
        InstanceSearchParam instanceSearchParam = new InstanceSearchParam();
        instanceSearchParam.setGroupOrModelId(param.getId());
        List<String> fields = new ArrayList<>();
        fields.add(CMDBTool.getFieldName(InstanceInfo::getData));
        instanceSearchParam.setFields(fields);
        List<InstanceInfoExtend> instanceInfoExtendList = instanceService.aggregateList(instanceSearchParam);
        List<VideoModelProperty> videoModelProperties = new ArrayList<>();
        VideoModelProperty videoModelProperty;
        for (InstanceInfoExtend instanceInfoExtend : instanceInfoExtendList) {
            videoModelProperty = JSON.parseObject(JSON.toJSONString(instanceInfoExtend.getData()), VideoModelProperty.class);
            videoModelProperty.setMw_modelName(instanceInfoExtend.getModelName());
            videoModelProperty.setCreateTime(instanceInfoExtend.getCreateTime());
            // 计算使用年限
            videoModelProperty.setMw_video_used_years(VideoUsedYearsEnum.getVideoUsedYearsByDate(videoModelProperty.getMw_video_commissioning_date()));
            videoModelProperties.add(videoModelProperty);
        }

        // 实例按照维度统计数据
        // 1、资产类型分组 按照modelName统计实例数量
        List<InstanceOverviewVo> instanceOverviewVoList = new ArrayList<>();
        instanceOverviewVoList.addAll(calcByDimension(videoModelProperties, CMDBTool.getFieldName(VideoModelProperty::getMw_modelName),
                Arrays.asList(CMDBTool.getFieldName(VideoModelProperty::getMw_video_area),
                        CMDBTool.getFieldName(VideoModelProperty::getMw_video_vendor),
                        CMDBTool.getFieldName(VideoModelProperty::getMw_video_used_years),
                        CMDBTool.getFieldName(VideoModelProperty::getMw_video_maintenance_times)),
                CMDBTool.getFieldName(VideoModelProperty::getMw_video_status)));
        instanceOverviewVoList.addAll(calcByDimension(videoModelProperties,
                CMDBTool.getFieldName(VideoModelProperty::getMw_video_status),
                CMDBTool.getFieldName(VideoModelProperty::getMw_modelName)));
        instanceOverviewVoList.addAll(calcByDimension(videoModelProperties, CMDBTool.getFieldName(VideoModelProperty::getMw_video_project)));
        return instanceOverviewVoList;
    }

    private List<InstanceOverviewVo> calcByDimension(List<VideoModelProperty> videoModelProperties, Object... dimensions) throws Exception {
        List<InstanceOverviewVo> resultList = new ArrayList<>();
        if (dimensions.length > 0) {
            if (dimensions[0] instanceof String) {
                InstanceOverviewVo instanceOverviewVo = new InstanceOverviewVo();
                instanceOverviewVo.setDimension((String) dimensions[0]);
                calcRecursion(instanceOverviewVo, videoModelProperties, 1, dimensions);
                resultList.add(instanceOverviewVo);
            } else if (dimensions[0] instanceof List) {
                List<String> dimensionList = (List<String>) dimensions[0];
                for (int i = 0; i < dimensionList.size(); i++) {
                    InstanceOverviewVo instanceOverviewVo = new InstanceOverviewVo();
                    instanceOverviewVo.setDimension(dimensionList.get(i));
                    calcRecursion(instanceOverviewVo, videoModelProperties, 1, dimensions);
                    resultList.add(instanceOverviewVo);
                }
            }
        }
        return resultList;
    }

    private Function<VideoModelProperty, String> getGroupFunction(String fieldName) throws Exception {
        Field field = VideoModelProperty.class.getDeclaredField(fieldName);
        field.setAccessible(true);
        return videoModelProperty -> {
            try {
                return field.get(videoModelProperty) == null ? "未知" : field.get(videoModelProperty).toString();
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Failed to extract field value,fieldName:" + fieldName);
            }
        };
    }

    private void calcRecursion(InstanceOverviewVo instanceOverviewVo, List<VideoModelProperty> videoModelProperties, int nextIndex, Object... dimensions) throws Exception {
        List<InstanceOverviewValueVo> values = new ArrayList<>();
        InstanceOverviewValueVo instanceOverviewValueVo;
        if (CMDBTool.getFieldName(VideoModelProperty::getMw_video_maintenance_times).equals(instanceOverviewVo.getDimension())) {
            List<VideoModelProperty> collect = videoModelProperties.stream().filter(s -> s != null && s.getMw_video_maintenance_times() != null).collect(Collectors.toList());
            collect.sort(Comparator.nullsLast(Comparator.comparing(VideoModelProperty::getMw_video_maintenance_times).reversed()));
            Map<String, Object> extentProperties;
            for (VideoModelProperty videoModelProperty : collect) {
                instanceOverviewValueVo = new InstanceOverviewValueVo();
                instanceOverviewValueVo.setKey(videoModelProperty.getMw_instanceName());
                instanceOverviewValueVo.setValue(videoModelProperty.getMw_video_maintenance_times());
                extentProperties = new HashMap<>();
                extentProperties.put(CMDBTool.getFieldName(VideoModelProperty::getMw_video_vendor), videoModelProperty.getMw_video_vendor());
                extentProperties.put(CMDBTool.getFieldName(VideoModelProperty::getMw_video_model), videoModelProperty.getMw_video_model());
                instanceOverviewValueVo.setExtentProperties(extentProperties);
                values.add(instanceOverviewValueVo);
                // 维修记录排名只保留10条数据
                if (values.size() == 10) {
                    break;
                }
            }
            instanceOverviewVo.setValues(values);
            return;
        }
        Map<String, List<VideoModelProperty>> key2VoListMap = videoModelProperties.stream().collect(Collectors.groupingBy(getGroupFunction(instanceOverviewVo.getDimension())));
        if (CMDBTool.getFieldName(VideoModelProperty::getMw_video_status).equals(instanceOverviewVo.getDimension())) {
            for (VideoInstanceStatusEnum videoInstanceStatusEnum : VideoInstanceStatusEnum.values()) {
                if (!key2VoListMap.containsKey(videoInstanceStatusEnum.getDesc())) {
                    key2VoListMap.put(videoInstanceStatusEnum.getDesc(), new ArrayList<>());
                }
            }
        }
        for (Map.Entry<String, List<VideoModelProperty>> entry : key2VoListMap.entrySet()) {
            instanceOverviewValueVo = new InstanceOverviewValueVo();
            instanceOverviewValueVo.setKey(entry.getKey());
            instanceOverviewValueVo.setValue(String.valueOf(entry.getValue().size()));
            // 模型分类计算较昨日趋势
            if (CMDBTool.getFieldName(VideoModelProperty::getMw_modelName).equals(instanceOverviewVo.getDimension())) {
                calcTrend(instanceOverviewValueVo, entry.getValue());
            }
            values.add(instanceOverviewValueVo);
            instanceOverviewValueVo.setSubOverviews(new ArrayList<>());
            if (dimensions.length > nextIndex) {
                InstanceOverviewVo subOverview;
                if (dimensions[nextIndex] instanceof String) {
                    subOverview = new InstanceOverviewVo();
                    subOverview.setDimension((String) dimensions[nextIndex]);
                    calcRecursion(subOverview, entry.getValue(), nextIndex + 1, dimensions);
                    instanceOverviewValueVo.getSubOverviews().add(subOverview);
                } else if (dimensions[nextIndex] instanceof List) {
                    List<String> dimensionList = (List<String>) dimensions[nextIndex];
                    for (int i = 0; i < dimensionList.size(); i++) {
                        subOverview = new InstanceOverviewVo();
                        subOverview.setDimension(dimensionList.get(i));
                        calcRecursion(subOverview, entry.getValue(), nextIndex + 1, dimensions);
                        instanceOverviewValueVo.getSubOverviews().add(subOverview);
                    }
                }
            }
        }
        if (CMDBTool.getFieldName(VideoModelProperty::getMw_video_vendor).equals(instanceOverviewVo.getDimension())) {
            values.sort(Comparator.comparing(InstanceOverviewValueVo::getValue).reversed());
        }
        instanceOverviewVo.setValues(values);
    }

    private void calcTrend(InstanceOverviewValueVo instanceOverviewValueVo, List<VideoModelProperty> videoModelPropertyList) {
        // 当前数量
        int currentSize = videoModelPropertyList.size();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        int todayDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
        // 昨日数量
        int yesterDaySize = videoModelPropertyList.stream().filter(data -> {
            calendar.setTime(data.getCreateTime());
            return calendar.get(Calendar.DAY_OF_YEAR) < todayDayOfYear;
        }).toList().size();

        Double trend;
        if (yesterDaySize == 0) {
            trend = 100.0;
        } else {
            trend = ((double) (currentSize - yesterDaySize) / yesterDaySize) * 100.0;
        }
        instanceOverviewValueVo.setTrent(trend + "%");
    }
}
